/*
 *  tree2.h
 *  
 *
 *  Created by Jason Hsu, Jian Wei Gan, and Weiping Zhang on 11/8/09.
 *  Copyright 2009. All rights reserved.
 *
 *	drawSphere found at: http://ozark.hendrix.edu/~burch/cs/490/sched/feb8/
 *
 *	TODO:
 *		make the "leaves" look better?
 *
 *
 *
 *
 *
 *
 */

#include "util/vec3f.h"
#include "util/math_util.h"
#include "util/color4f.h"
#include "math.h"
#include "texture.h"

class Tree2
{
private:
	Vec3f myPosition;
	GLfloat myHeight;	//technically, this is the height of the trunk only, excluding base
	GLUquadric* quad;
	bool isOutlineEnabled;
	GLfloat myLineWidth;
	GLfloat myScaleFactor;
	struct tm* myTimeInfo;
	Vec3f mySunPos;
	
public:
	Tree2()
	{
		myPosition = Vec3f();
		myHeight = randFloatRange(2.0, 5.0);
		quad = gluNewQuadric();
		isOutlineEnabled = true;
		myLineWidth = 3.0f;
		myScaleFactor = 1.0f;
		
		myTimeInfo = Utility::getTime();
		GLfloat sun_pos_x = (5.0f/18.0f) * (myTimeInfo->tm_hour*60.0f + myTimeInfo->tm_min) - 200.0f;
		GLfloat sun_pos_y = -(1.0f/400.0f) * pow(sun_pos_x, 2) + 100.0f;
		mySunPos = Vec3f(sun_pos_x, sun_pos_y, 0);
	}
	
	~Tree2()
	{
	}
	
	void setPosition(GLfloat x, GLfloat y, GLfloat z)
	{
		myPosition *= 0.0f;
		myPosition += Vec3f(x, y, z);
	}
	
	void scale()
	{
		myScaleFactor = 3.0f;
	}
	
	void draw()
	{
//		switchToOrtho();
		glPushMatrix();
		
		Color4f brownOutline = Color4f(0.12f, 0.05f, 0.0f);
		glColor3f(0.22f, 0.15f, 0.1f);	//brown
		glTranslatef(myPosition[0], myPosition[1], myPosition[2]);	//position in xz-plane
		glScalef(myScaleFactor, myScaleFactor, myScaleFactor);

		//###################################################################### BASE OF TRUNK
		glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
		gluCylinder(quad, 0.4, 0.3, 1.0, 20, 20);
		
		if(isOutlineEnabled)
		{
			enableOutlining(brownOutline);
			gluCylinder(quad, 0.4f, 0.3f, 1.0f, 20, 20);
			disableOutlining();
		}
		
		glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		
		//############################################### TRUNK
		glColor3f(0.22f, 0.15f, 0.1f);	//brown
		glTranslatef(0.0f, 1.0f, 0.0f);
		glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
		gluCylinder(quad, 0.3, 0.18, myHeight, 20, 20);
		
		if(isOutlineEnabled)
		{
			enableOutlining(brownOutline);
			gluCylinder(quad, 0.3, 0.18, myHeight, 20, 20);
			disableOutlining();
		}
		
		glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		
		//###################################### BRANCH (right)
		glColor3f(0.22f, 0.15f, 0.1f);	//brown
		glTranslatef(0.0f, myHeight-0.18f, 0.0f);
		GLfloat shear[] = {
			1, 0.2, 0, 0, 
			0.2, 1, 0, 0, 
			0, 0, 1, 0, 
			0, 0, 0, 1 };
		glMultMatrixf(shear);
		glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
		glRotatef(50.0f, 0.0f, 1.0f, 0.0f);
		gluCylinder(quad, 0.24, 0.04, 3.0, 20, 20);
		
		if(isOutlineEnabled)
		{
			enableOutlining(brownOutline);
			gluCylinder(quad, 0.24, 0.04, 3.0, 20, 20);
			disableOutlining();
		}
		
		glRotatef(-50.0f, 0.0f, 1.0f, 0.0f);
		glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		GLfloat unshear[] = {
			1, -0.2, 0, 0, 
			-0.2, 1, 0, 0, 
			0, 0, 1, 0, 
			0, 0, 0, 1 };
		glMultMatrixf(unshear);
		
		//##################################### BRANCH (left)
		glColor3f(0.22f, 0.15f, 0.1f);	//brown
		GLfloat shear1[] = {
			1, 0.4, 0, 0, 
			0.4, 1, 0, 0, 
			0, 0, 1, 0, 
			0, 0, 0, 1 };
		glMultMatrixf(shear1);
		glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
		glRotatef(-40.0f, 0.0f, 1.0f, 0.0f);
		gluCylinder(quad, 0.2, 0.04, 3.0, 20, 20);
		
		if(isOutlineEnabled)
		{
			enableOutlining(brownOutline);
			gluCylinder(quad, 0.2, 0.04, 3.0, 20, 20);
			disableOutlining();		
		}
		
		glRotatef(40.0f, 0.0f, 1.0f, 0.0f);
		glRotatef(90.0f, 1.0f, 0.0f, 0.0f);
		GLfloat unshear1[] = {
			1, -0.4, 0, 0, 
			-0.4, 1, 0, 0, 
			0, 0, 1, 0, 
			0, 0, 0, 1 };
		glMultMatrixf(unshear1);
		
		//########################################## LEAVES		
		glColor3f(0.2f, 0.32f, 0.15f);
		glTranslatef(2.2f, 2.3f, 0.0f);
		drawSphere(1.3f, 40.0f, 40.0f, 0);
		glTranslatef(-4.0f, 0.0f, 0.0f);
		glColor3f(0.2f, 0.32f, 0.15f);
		drawSphere(1.1f, 40.0f, 40.0f, 1);
		glTranslatef(2.0f, 1.0f, 0.0f);
		glColor3f(0.2f, 0.32f, 0.15f);
		drawSphere(2.0f, 40.0f, 40.0f, 2);
		glPopMatrix();
//		switchToProj();
	}
	
	void update()
	{
		GLfloat sun_pos_x = (5.0f/18.0f) * (myTimeInfo->tm_hour*60.0f + myTimeInfo->tm_min) - 200.0f;
		GLfloat sun_pos_y = -(1.0f/400.0f) * pow(sun_pos_x, 2) + 100.0f;
		mySunPos *= 0.0f;
		mySunPos += Vec3f(sun_pos_x, sun_pos_y, 0.0f);
	}
	
	void drawSphere(GLfloat r, int lats, int longs, int flag) 
	{		
		int i, j;
		GLfloat cellShadeVal, dotProduct;
		Vec3f lightVector(mySunPos[0], mySunPos[1], mySunPos[3]);
		lightVector = lightVector.normalize();
		Vec3f temp(-11, 30, lightVector[2]);
		temp = temp.normalize();
		Vec3f temp1(10, 45, lightVector[2]);
		temp1 = temp1.normalize();
		
		for(i = 0; i <= lats; i++) {
			GLfloat lat0 = M_PI * (-0.5 + (GLfloat) (i - 1) / lats);
			GLfloat z0  = sin(lat0);
			GLfloat zr0 =  cos(lat0);
			
			GLfloat lat1 = M_PI * (-0.5 + (GLfloat) i / lats);
			GLfloat z1 = sin(lat1);
			GLfloat zr1 = cos(lat1);
			
			glBegin(GL_QUAD_STRIP);
			for(j = 0; j <= longs; j++) {
				GLfloat lng = 2 * M_PI * (GLfloat) (j - 1) / longs;
				GLfloat x = cos(lng);
				GLfloat y = sin(lng);
				
				Vec3f normal(x*zr0, z0, y*zr0);
				//				glNormal3f(x * zr0, y * zr0, z0);
				dotProduct = (normal.normalize()).dot(lightVector);
				cellShadeVal = getGradientVal(dotProduct, flag, lightVector[0], temp[0], temp1[0]);
				glColor3f(0.05f+cellShadeVal, 0.17f+cellShadeVal, 0.0f+cellShadeVal);
				glVertex3f(r * x * zr0, r * z0, r * y * zr0);
				
				normal = Vec3f(x*zr1, z1, y*zr1);
				//				glNormal3f(x * zr1, y * zr1, z1);
				dotProduct = (normal.normalize()).dot(lightVector);
				cellShadeVal = getGradientVal(dotProduct, flag, lightVector[0], temp[0], temp1[0]);
				glColor3f(0.05f+cellShadeVal, 0.17f+cellShadeVal, 0.0f+cellShadeVal);
				glVertex3f(r * x * zr1, r * z1, r * y * zr1);
			}
			glEnd();
		}
		if(isOutlineEnabled)
		{
			Color4f greenOutline = Color4f(0.05f, 0.017f, 0.0f);
			enableOutlining(greenOutline);
			for(i = 0; i <= lats; i++) {
				GLfloat lat0 = M_PI * (-0.5 + (GLfloat) (i - 1) / lats);
				GLfloat z0  = sin(lat0);
				GLfloat zr0 =  cos(lat0);
				
				GLfloat lat1 = M_PI * (-0.5 + (GLfloat) i / lats);
				GLfloat z1 = sin(lat1);
				GLfloat zr1 = cos(lat1);
				
				glBegin(GL_QUAD_STRIP);
				for(j = 0; j <= longs; j++) {
					GLfloat lng = 2 * M_PI * (GLfloat) (j - 1) / longs;
					GLfloat x = cos(lng);
					GLfloat y = sin(lng);
					glVertex3f(r * x * zr0, r * z0, r * y * zr0);
					glVertex3f(r * x * zr1, r * z1, r * y * zr1);
				}
				glEnd();
			}
			disableOutlining();		
		}
	}
	
	GLfloat getGradientVal(GLfloat dotProduct, int flag, GLfloat lightVal, GLfloat treeVal0, GLfloat treeVal1)
	{
		if(flag == 0)
		{
			if(lightVal < treeVal0)
				return 0.01;
		}
		else if(flag == 1)
		{
			if(lightVal > treeVal1)
				return 0.01;
		}
		
		if(dotProduct < 0.15)
			return 0.01;
		else
			return 0.1;
	}
	
	void enableOutlining(Color4f color)
	{
		glEnable(GL_CULL_FACE);								// Enable face culling
		glEnable (GL_BLEND);								// Enable Blending
		glBlendFunc (GL_SRC_ALPHA ,GL_ONE_MINUS_SRC_ALPHA);	// Set blend mode
		glPolygonMode (GL_BACK, GL_LINE);					// Draw Backfacing Polygons As Wireframes
		glLineWidth (myLineWidth);							// Set The Line Width
		glCullFace (GL_FRONT);								// Don't Draw Any Front-Facing Polygons
		glDepthFunc (GL_LEQUAL);							// Change The Depth Mode
		glColor3f(color[0], color[1], color[2]);						// Set The Outline Color
	}
	
	void disableOutlining()
	{
		glDepthFunc (GL_LESS);				// Reset The Depth-Testing Mode
		glCullFace (GL_BACK);				// Reset The Face To Be Culled
		glDisable(GL_CULL_FACE);			// Disable face culling
		glPolygonMode (GL_BACK, GL_FILL);	// Reset Back-Facing Polygon Drawing Mode
		glDisable (GL_BLEND);				// Disable Blending
	}
	
	void switchToOrtho()
	{
		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		
		glOrtho(-75.0f, 75.0f, -15.75, 15.75, 0.1, 500.0);
		
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
	}
	
	void switchToProj()
	{
		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
	}
};
